# https://gitee.com/yueyinqiu5990/tj12413601/blob/master/assignment1/question2/integration_calculator.py
import abc
import typing

import integration_problem


class Integration1dCalculatorAdaptive(abc.ABC):
    @abc.abstractmethod
    def _generate_steps_endlessly(
            self,
            problem: integration_problem.IntegrationProblem1d) \
            -> typing.Generator[float, None, None]:
        raise NotImplementedError

    def calculate_with_steps(
            self,
            problem: integration_problem.IntegrationProblem1d,
            end_condition: float,
            min_step_count: int = 2) -> typing.Generator[float, None, None]:
        if end_condition <= 0:
            raise ValueError("end_condition should be greater than zero.")
        if min_step_count <= 0:
            raise ValueError("min_step_count should be greater than one.")
        steps = self._generate_steps_endlessly(problem)
        previous = None
        for previous in steps:
            yield previous
            # 下方一定会先 yield 一次，
            # 因此此处到了 2 就该跳出了。
            if min_step_count <= 2:
                break
            min_step_count -= 1
        assert previous is not None
        for step in steps:
            yield step
            if abs(step - previous) < end_condition:
                break
            previous = step

    def calculate(
            self,
            problem: integration_problem.IntegrationProblem1d,
            end_condition: float,
            min_step_count: int = 2) -> float:
        step = None
        for step in self.calculate_with_steps(problem, end_condition, min_step_count):
            pass
        assert step is not None
        return step

    @staticmethod
    def endless_range(start: int, step: int = 1):
        while True:
            yield start
            start += step
